#include "app.h"

UART_HandleTypeDef huart1;
uint8_t txBuffer[TX_BUFFER_SIZE];
uint16_t frameBuffer[OV7670_WIDTH * OV7670_HEIGHT] = {0};
/**
 * @brief       复位LCD的写入位置至坐标为(0, 0)的像素点
 * @param       无
 * @retval      无
 */
static void demo_reset_lcd(void) {
#ifdef ENABLE_LCD_SHOW_IMAGE
    int width = ov7670_get_output_width();
    int height = ov7670_get_output_height();
    LCDScanDir(DFT_SCAN_DIR);
    LCDSetWindow(0, 0, width, height);
    LCDSetCursor(0, 0);
    LCDWriteRamPrepare();
#endif
}

// 发送数据函数
void SendData(uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4) {
    uint8_t txData[64];
    uint8_t checksum;
    uint8_t length = 4;
    // 构建数据帧
    txData[0] = 0x33;   // 帧头
    txData[1] = 0x01;   // 功能码
    txData[2] = length; // 数据长度
    // 将short_data和u16数据转换为字节并填充到txData
    txData[3] = data1 & 0xFF; // x的低字节

    txData[4] = data2 & 0xFF; // y的低字节

    txData[5] = data3 & 0xFF; // data3的低字节

    txData[6] = data4 & 0xFF; // data4的低字节
    // 计算校验和
    checksum = txData[3] + txData[4] + txData[5] + txData[6];

    txData[7] = checksum;

    txData[8] = 0xFF; // 帧尾

    // 发送数据
    HAL_UART_Transmit(&GetUartHandle(USART1_INDEX)->huart, txData, 9, HAL_MAX_DELAY);
}

void DrawPicture(uint16_t width, uint16_t height, uint16_t* pBuf) {
    for (uint16_t i = 0; i < width; i++) {
        for (uint16_t j = 0; j < height; j++) {
            LCD->LCD_RAM = pBuf[i * width + j];
        }
    }
}

// 计算太阳坐标
static uint16_t frameCnt = 0;
uint8_t CalcSunPosition(uint16_t* pBuf, uint16_t width, uint16_t height, uint16_t* sunX, uint16_t* sunY, uint16_t* Min, uint16_t* center) {
    uint16_t tmpBuf;
    uint8_t grayMax = 0;
    uint8_t minavggray = 255;
    int target_width = 3;
    int half_target_width = target_width / 2;

    // 预计算一些值以减少循环内的计算
    int target_width_squared = target_width * target_width;

    for (uint16_t i = half_target_width; i < width - half_target_width; i++) {

        for (uint16_t j = half_target_width; j < height - half_target_width; j++) {
            uint16_t sumGray = 0;

            // 计算3x3区域的灰度均值
            for (int k = -half_target_width; k <= half_target_width; k++) {
                for (int l = -half_target_width; l <= half_target_width; l++) {
                    tmpBuf = pBuf[(i + k) * width + (j + l)];

                    // 从 RGB 565 获取 RGB 分量
                    uint8_t r = (tmpBuf >> 11) & 0x1F;
                    uint8_t g = (tmpBuf >> 5) & 0x3F;
                    uint8_t b = tmpBuf & 0x1F;

                    // 将 5 位和 6 位值扩展到 8 位
                    r = (r * 255) / 31;
                    g = (g * 255) / 63;
                    b = (b * 255) / 31;

                    // RGB 转灰度 (整数运算)
                    uint8_t gray = (uint8_t)((299 * r + 587 * g + 114 * b) / 1000);
                    sumGray += gray;
                }
            }

            uint8_t avgGray = sumGray / target_width_squared;

            if (avgGray > grayMax) {
                grayMax = avgGray;
                *sunX = j;
                *sunY = i;
                *center = avgGray;
            }

            if (avgGray < minavggray) {
                minavggray = avgGray;
                *Min = minavggray;
            }
        }
    }

    frameCnt++;
    return 0;
}
void DrawTarget(uint16_t targetX, uint16_t targetY, uint16_t Gmin, uint16_t Gc) {
    char str[50];
    sprintf(str, "Position:(%3d,%3d,%3d,%3d)", targetX, targetY, Gmin, Gc);
//		Send(&targetX,sizeof(targetX));
//		Send(&targetY,sizeof(targetY));
//    LOG("%s\r\n", str);
#ifdef ENABLE_LCD_SHOW_IMAGE
    LCDDrawCircle(targetX, targetY, 10, RED);
    LCDShowString(10, 354, 350, 24, 24, str, RED);
#endif
}

void DrawFps() {
    char str[10];
    sprintf(str, "FPS:%3d", frameCnt);
//    LOG("%s\r\n", str);
#ifdef ENABLE_LCD_SHOW_IMAGE
    LCDShowString(10, 378, 150, 24, 24, str, RED);
#endif
    frameCnt = 0;
}

void APP_Init(void) {
    BSP_Init();
    MX_IWDG_Init();
    // 添加任务
    AddTask("IWDG_Feed", 1500, IWDG_Feed, NULL);
    AddTask("Calc_FPS", 1000, DrawFps, NULL);
}

void APP_Scheduler(void) {
    uint16_t maxX, maxY, centerG, Min;
    TaskScheduler();
    /* 将获取到的图像数据，显示至LCD */
    if (OV7670GetFrame((uint32_t)&frameBuffer, ((ov7670_get_type_t){.bufferType = OV7670_GET_TYPE_BUFFER_FRAME, .bufferLength = OV7670_GET_TYPE_BUFFER_16B, .bufferInc = OV7670_GET_TYPE_BUFFER_INC}), demo_reset_lcd) == OV7670_EOK) {
#ifdef ENABLE_SERIAL_SHOW_IMAGE
        SendImageStart(OV7670_WIDTH / 2, OV7670_HEIGHT / 2);
        for (uint16_t row = 0; row < OV7670_WIDTH / 2; row++) {
            SendImageData(frameBuffer[row], OV7670_HEIGHT / 2);
        }
        SendImageFinish();
#endif
        DrawPicture(ov7670_get_output_width(), ov7670_get_output_height(), frameBuffer);
        CalcSunPosition(frameBuffer, ov7670_get_output_width(), ov7670_get_output_height(), &maxX, &maxY, &Min, &centerG);
        SendData(maxX, maxY, Min, centerG);
        DrawTarget(maxX, maxY, Min, centerG);
    }
}